<template>
  <a-flex vertical gap="small" style="height: 100%">
    <a-flex gap="small">
      <a-space-compact block>
        <a-input-group compact>
          <a-select ref="select" v-model:value="method" style="width: 120px">
            <a-select-option value="GET">GET</a-select-option>
            <a-select-option value="POST">POST</a-select-option>
            <a-select-option value="PUT">PUT</a-select-option>
            <a-select-option value="PATCH">PATCH</a-select-option>
            <a-select-option value="DELETE">DELETE</a-select-option>
            <a-select-option value="HEAD">HEAD</a-select-option>
            <a-select-option value="OPTIONS">OPTIONS</a-select-option>
          </a-select>
          <a-input v-model:value="url" placeholder="请输入API地址" />
        </a-input-group>
        <a-button :loading="loading" type="primary" :disabled="!url" @click="sendRequest"
          >发送请求
        </a-button>
      </a-space-compact>
    </a-flex>
    <a-flex vertical style="height: 350px">
      <a-menu v-model:selectedKeys="activeTab" mode="horizontal" style="width: 100%">
        <a-menu-item key="params">Params</a-menu-item>
        <a-menu-item key="headers">Headers</a-menu-item>
        <a-menu-item key="body">Body</a-menu-item>
      </a-menu>
      <a-flex flex="1" style="background-color: #ffffff">
        <a-table
          :columns="columns"
          :pagination="false"
          size="small"
          :data-source="paramsData"
          :scroll="{ y: 260 }"
          bordered
          :rowKey="(record) => record.key"
          v-if="activeTab[0] === 'params'"
        >
          <template #bodyCell="{ column, record, index }">
            <template v-if="column.dataIndex === 'key'">
              <a-input
                v-model:value="obj[record.id + '-key']"
                size="small"
                @blur="handleBlur(record, paramsData)"
              />
            </template>
            <template v-if="column.dataIndex === 'value'">
              <a-flex gap="small">
                <a-input
                  v-model:value="obj[record.id + '-value']"
                  size="small"
                  @blur="handleBlur(record, paramsData, true)"
                />
                <a-button
                  danger
                  type="primary"
                  size="small"
                  v-if="index > 0"
                  @click="del(index, record, paramsData)"
                >
                  删除
                </a-button>
              </a-flex>
            </template>
          </template>
        </a-table>
        <a-table
          :columns="columns"
          :data-source="headersData"
          size="small"
          :pagination="false"
          :scroll="{ y: 300 }"
          bordered
          :rowKey="(record) => record.key"
          v-if="activeTab[0] === 'headers'"
        >
          <template #bodyCell="{ column, record, index }">
            <template v-if="column.dataIndex === 'key'">
              <a-input
                v-model:value="obj[record.id + '-key']"
                size="small"
                @blur="handleBlur(record, headersData)"
              />
            </template>
            <template v-if="column.dataIndex === 'value'">
              <a-input
                v-model:value="obj[record.id + '-value']"
                size="small"
                @blur="handleBlur(record, headersData, true)"
              />
              <a-button
                danger
                type="primary"
                v-if="index > 0"
                @click="del(index, record, headersData)"
                >删除
              </a-button>
            </template>
          </template>
        </a-table>
        <a-flex vertical gap="small" v-if="activeTab[0] === 'body'" style="width: 100%">
          <a-flex
            style="background-color: #ffffff; height: 40px; padding-left: 15px"
            align="center"
          >
            <a-radio-group v-model:value="bodyType" name="radioGroup">
              <a-radio checked value="1">form-data</a-radio>
              <a-radio value="2">x-www-form-urlencoded</a-radio>
              <a-radio value="3">raw</a-radio>
            </a-radio-group>
            <a-select
              ref="select"
              style="width: 110px"
              v-model:value="bodyTypeFormater"
              v-if="bodyType === '3'"
              size="small"
            >
              <a-select-option value="JSON">JSON</a-select-option>
              <a-select-option value="XML">XML</a-select-option>
              <a-select-option value="Text">Text</a-select-option>
              <a-select-option value="JavaScript">JavaScript</a-select-option>
              <a-select-option value="HTML">HTML</a-select-option>
            </a-select>
          </a-flex>
          <a-table
            v-if="bodyType === '1' || bodyType === '2'"
            :columns="columns"
            :data-source="bodyData"
            size="small"
            :pagination="false"
            :scroll="{ y: 240 }"
            bordered
            :rowKey="(record) => record.key"
            style="height: 100%"
          >
            <template #bodyCell="{ column, record, index }">
              <template v-if="column.dataIndex === 'key'">
                <a-input
                  v-model:value="obj[record.id + '-key']"
                  size="small"
                  @blur="handleBlur(record, bodyData)"
                />
              </template>
              <template v-if="column.dataIndex === 'value'">
                <a-flex gap="small">
                  <a-input
                    v-model:value="obj[record.id + '-value']"
                    size="small"
                    @blur="handleBlur(record, bodyData, true)"
                  />
                  <a-button
                    type="primary"
                    danger
                    size="small"
                    v-if="index > 0"
                    @click="del(index, record, bodyData)"
                    >删除
                  </a-button>
                </a-flex>
              </template>
            </template>
          </a-table>
          <a-textarea
            v-else
            v-model:value="bodyDataStr"
            style="width: 100%; height: 100%"
          ></a-textarea>
        </a-flex>
      </a-flex>
    </a-flex>
    <a-flex flex="1" vertical>
      <a-flex>
        <a-menu
          v-model:selectedKeys="responseShowType"
          mode="horizontal"
          style="width: 100%"
          @click="parseResponse"
        >
          <a-sub-menu key="Pretty" title="Pretty">
            <a-menu-item key="Pretty-Text">Text</a-menu-item>
            <a-menu-item key="Pretty-JSON">JSON</a-menu-item>
            <a-menu-item key="Pretty-HTML">HTML</a-menu-item>
          </a-sub-menu>
          <a-menu-item key="Raw">Raw</a-menu-item>
          <a-menu-item key="PreView">PreView</a-menu-item>
        </a-menu>
      </a-flex>
      <a-flex class="responseShowBox" flex="1">
        <div
          style="width: 100%; overflow-y: auto; background-color: #ffffff"
          :style="{ height: containerHeight + 'px' }"
        >
          <iframe
            v-if="responseShowType[0] === 'PreView'"
            :srcdoc="responseShow"
            class="preViewBox"
          ></iframe>
          <JsonViewer
            v-else-if="responseShowType[0] === 'Pretty-JSON'"
            class="jsonTextArea"
            :json="responseShow"
          />
          <div v-else style="width: 100%">
            {{ responseShow }}
          </div>
        </div>
      </a-flex>
    </a-flex>
  </a-flex>
</template>

<script setup>
import { onMounted, onUnmounted, ref } from 'vue'
import { html } from 'js-beautify'
import JsonViewer from '../components/JsonViewer.vue'

const obj = ref({})
const url = ref('')
const method = ref('GET')
const activeTab = ref(['params'])
const bodyType = ref('1')
const loading = ref(false)
const bodyTypeFormater = ref('JSON')
const paramsData = ref([{ id: '1', key: '', value: '' }])
const headersData = ref([{ id: '2', key: '', value: '' }])
const bodyData = ref([{ id: '3', key: '', value: '' }])
const bodyDataStr = ref('{}')
const response = ref('')
const responseObj = ref({})
const responseShow = ref('')
const responseShowType = ref(['Pretty-Text'])
const containerHeight = ref(0)
const parseResponse = (item) => {
  if (!response || !response.value) {
    return
  }
  let selectKey
  if (item) {
    selectKey = item.key
  } else {
    selectKey = responseShowType.value[0]
  }
  switch (selectKey) {
    case 'Pretty-HTML':
      try {
        responseShow.value = html(response.value, { indent_size: 2 })
      } catch (e) {
        responseShow.value = response.value
      }
      break
    default:
      responseShow.value = response.value
  }
}

const columns = [
  { title: 'Key', dataIndex: 'key', key: 'key', width: 200 },
  { title: 'Value', dataIndex: 'value', key: 'value' }
]

const sendRequest = async () => {
  if (!url.value) {
    return
  }
  loading.value = true
  try {
    let bodyObj
    if (bodyType.value === '1' || bodyType.value === '2') {
      bodyObj = bodyData.value.reduce((acc, item) => {
        if (item.key) {
          acc[item.key] = item.value
        }
        return acc
      }, {})
    } else {
      bodyObj = bodyDataStr.value
    }
    const headerObj = headersData.value.reduce((acc, item) => {
      if (item.key) {
        acc[item.key] = item.value
      }
      return acc
    }, {})
    headerObj.Connection = 'keep-alive'
    headerObj.Accept = '*/*'

    // 根据 bodyType 和 bodyTypeFormater 设置 Content-Type
    if (bodyType.value === '1') {
      headerObj['Content-Type'] = 'multipart/form-data'
    } else if (bodyType.value === '2') {
      headerObj['Content-Type'] = 'application/x-www-form-urlencoded'
    } else if (bodyType.value === '3') {
      switch (bodyTypeFormater.value) {
        case 'JSON':
          headerObj['Content-Type'] = 'application/json'
          break
        case 'XML':
          headerObj['Content-Type'] = 'application/xml'
          break
        case 'Text':
          headerObj['Content-Type'] = 'text/plain'
          break
        case 'JavaScript':
          headerObj['Content-Type'] = 'application/javascript'
          break
        case 'HTML':
          headerObj['Content-Type'] = 'text/html'
          break
        default:
          headerObj['Content-Type'] = 'application/json' // 默认值
      }
    }

    const urlValue = url.value
    const config = {
      method: method.value,
      url: urlValue.toString().startsWith('http') ? urlValue : 'http://' + urlValue,
      params: paramsData.value.reduce((acc, item) => {
        if (item.key) {
          acc[item.key] = item.value
        }
        return acc
      }, {}),
      headers: headerObj,
      data: bodyObj
    }

    responseObj.value = await window.api.axiosRequest(config)
    response.value = responseObj.value.data
    parseResponse()
  } catch (error) {
    console.log(error)
    response.value = `Error: ${error.message}`
  } finally {
    loading.value = false
  }
}

const handleBlur = (record, dataSource, isValue = false) => {
  const k = record.id + (isValue ? '-value' : '-key')
  if (!isValue) {
    record.key = obj.value[k]
  } else {
    record.value = obj.value[k]
  }
  if (
    (record.key || record.value) &&
    !dataSource.some(
      (item) =>
        (item.key === '' || item.key === undefined) &&
        (item.value === '' || item.value === undefined)
    )
  ) {
    dataSource.push({ id: getUUid(), key: '', value: '' })
  }
}

const del = (index, record, dataSource) => {
  dataSource.splice(index, 1)
  delete obj.value[record.id + '-key']
  delete obj.value[record.id + '-value']
}

const getUUid = () => {
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
    const r = (Math.random() * 16) | 0
    const v = c === 'x' ? r : (r & 0x3) | 0x8
    return v.toString(16)
  })
}

const calculateContainerHeight = () => {
  containerHeight.value = 0
  setTimeout(() => {
    const cardElement = document.querySelector('.responseShowBox')
    if (cardElement) {
      containerHeight.value = cardElement.offsetHeight
    }
  })
}

onMounted(() => {
  calculateContainerHeight()
  window.addEventListener('resize', calculateContainerHeight)
})

onUnmounted(() => {
  window.removeEventListener('resize', calculateContainerHeight)
})
</script>

<style scoped>
.ant-input-group :deep() {
  display: flex;
}

.ant-tabs :deep(.ant-tabs-content) {
  height: 100%;
}

.ant-input :deep() {
  border-radius: 0 !important;
}

.ant-input :deep() {
  resize: none !important;
}

.responseShowBox {
  margin-top: 2px;
}

.preViewBox {
  width: 100%;
  height: 100%;
  border: none;
  background-color: #ffffff;
}

</style>
